﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
//using UnityEngine;

namespace USClientKernel.Common.Helper
{
    public delegate bool UpdateResult(float _value);
    public delegate void SetResultObj(CSharpLuaTable _t);
    public delegate void WriteCountUpdater(int _offset);

    public class ParseResult
    {
        private float m_fTotal;
        private float m_fCurrent;
        private float m_fProgress;
        private bool m_bIsDown;
        private CSharpLuaTable m_pObj;

        public bool IsDown { get { return m_bIsDown; } }
        public float Total { get { return m_fTotal; } }
        public float Current { get { return m_fCurrent; } }
        public float Progress { get { return m_fProgress; } }
        public CSharpLuaTable Obj { get { return m_pObj; } }

        public ParseResult(float _fTotal, out UpdateResult _ur, out SetResultObj _sro)
        {
            m_fTotal = _fTotal;
            m_fCurrent = 0.0f;
            m_fProgress = 0.0f;
            m_bIsDown = false;
            _sro = _SetResult;
            _ur = _Update;
        }

        private void _SetResult(CSharpLuaTable _t)
        {
            m_pObj = _t;
        }

        private bool _Update(float value)
        {
            m_fCurrent = value;
            if (0.0f != m_fTotal)
                m_fProgress = m_fCurrent / m_fTotal;
            if (1.0f == m_fProgress)
                m_bIsDown = true;
            return m_bIsDown;
        }
    }


    public class WriteResult
    {
        private float m_fTotal;
        private float m_fCurrent;
        private float m_fProgress;
        private bool m_bIsDown;

        public bool IsDown { get { return m_bIsDown; } }
        public float Total { get { return m_fTotal; } }
        public float Current { get { return m_fCurrent; } }
        public float Progress { get { return m_fProgress; } }

        public WriteResult(float _fTotal, out UpdateResult _ur)
        {
            m_fTotal = _fTotal;
            m_fCurrent = 0.0f;
            m_fProgress = 0.0f;
            m_bIsDown = false;
            _ur = _Update;
        }

        private bool _Update(float value)
        {
            m_fCurrent = value;
            if (0.0f != m_fTotal)
                m_fProgress = m_fCurrent / m_fTotal;
            if (1.0f == m_fProgress)
                m_bIsDown = true;
            return m_bIsDown;
        }
    }


    public class CSharpLuaTable
    {
        private float m_fTotalParse;
        private float m_fCurrentPares;
        private float m_fTotalWrite;
        private float m_fCurrentWrite;
        private WriteCountUpdater m_updater;

        private FileStream m_pFile;
        private StreamWriter m_pWriter;
        private Dictionary<string, string> m_dictValue;
        private Dictionary<string, CSharpLuaTable> m_dictTable;

        private UpdateResult m_pParseUpdate;
        private UpdateResult m_pWriteUpdate;

        private Thread m_pThread;


        /// <summary>
        /// 加载Lua的文件成C#结构
        /// </summary>
        /// <param name="_filePath">加载Lua文件的地址</param>
        /// <returns>返回解析结果</returns>
        public static CSharpLuaTable ParseFromFile(string _filePath)
        {
            string lstr = File.ReadAllText(@_filePath);
            lstr = Regex.Replace(lstr, @"[\n\r\t\s]", "");
            return ParseFromString(lstr);
        }


        /// <summary>
        /// 异步加载Lua的文件成C#结构
        /// </summary>
        /// <param name="_filePath">加载Lua文件的地址</param>
        /// <returns>返回解析结果</returns>
        public static ParseResult ParseFormFileAsync(string _filePath)
        {
            string lstr = File.ReadAllText(@_filePath);
            lstr = Regex.Replace(lstr, @"[\n\r\t\s]", "");
            return ParseFromStringAsync(lstr);
        }


        /// <summary>
        /// 加载Lua的文件成C#结构加强版，速度要快些但有限制
        /// local table = {
        ///     ...
        /// }
        /// </summary>
        /// <param name="_filePath">加载Lua文件的地址</param>
        /// <returns>返回解析结果</returns>
        public static CSharpLuaTable ParseFromFileEx(string _filePath)
        {
            string lstr = File.ReadAllText(@_filePath);
            lstr = Regex.Replace(lstr, @"[\n\r\t\s]", "");
            return ParseFromStringEx(lstr);
        }


        /// <summary>
        /// 异步加载Lua的文件成C#结构加强版，速度要快些但有限制
        /// local table = {
        ///     ...
        /// }
        /// </summary>
        /// <param name="_filePath">加载Lua文件的地址</param>
        /// <returns>返回解析结果</returns>
        public static ParseResult ParseFormFileExAsync(string _filePath)
        {
            string lstr = File.ReadAllText(@_filePath);
            lstr = Regex.Replace(lstr, @"[\n\r\t\s]", "");
            return ParseFromStringExAsync(lstr);
        }


        /// <summary>
        /// 同步加载Lua的string成C#结构
        /// </summary>
        /// <param name="_lstr">Lua的table字符串</param>
        /// <param name="_parser">解析进度的更新代理函数，除非非常了解内部机制，否则忽略次参数</param>
        /// <returns>返回解析结果</returns>
        public static CSharpLuaTable ParseFromString(string _lstr, Action _parser = null)
        {
            return new CSharpLuaTable(_lstr, _parser);
        }


        /// <summary>
        /// 异步加载Lua的String成C#结构
        /// </summary>
        /// <param name="_lstr">Lua的table字符串</param>
        /// <returns>返回解析结果</returns>
        public static ParseResult ParseFromStringAsync(string _lstr)
        {
            UpdateResult ur = null;
            SetResultObj sro = null;
            float len = _lstr.Length;
            ParseResult pr = new ParseResult(len, out ur, out sro);
            CSharpLuaTable table = new CSharpLuaTable(len, ur);
            sro(table);

            table.Thread = new Thread(new ThreadStart(() => 
            {
                table.ParseLuaTable(_lstr);
                table.Thread.Abort();
            }));
            table.Thread.Start();

            return pr;
        }


        /// <summary>
        /// 同步加载Lua的String成C#结构加强版，速度要快些但有限制
        /// local table = {
        ///     ...
        /// }
        /// </summary>
        /// <param name="_lstr">Lua的table字符串</param>
        /// <returns>返回解析结果</returns>
        public static CSharpLuaTable ParseFromStringEx(string _lstr)
        {
            return _ParseLuaTableEx(_lstr);
        }


        /// <summary>
        /// 异步加载Lua的String成C#结构加强版，速度要快些但有限制
        /// local table = {
        ///     ...
        /// }
        /// </summary>
        /// <param name="_lstr">Lua的table字符串</param>
        /// <returns>返回解析结果</returns>
        public static ParseResult ParseFromStringExAsync(string _lstr)
        {
            _lstr = _Preproccess(_lstr);
            return ParseFromStringAsync(_lstr);
        }


        /// <summary>
        /// 同步将C#结果写入到lua中
        /// </summary>
        /// <param name="_path">写入路径</param>
        public void WriteToFile(string _path)
        {
            _BeginWriteFile(_path);
            _WriteLuaTable(m_pWriter, this);
            _EndWriteFile();
        }


        /// <summary>
        /// 异步将C#结果写入到lua中
        /// </summary>
        /// <param name="_path">写入路径</param>
        /// <returns>返回写入结果</returns>
        public WriteResult WriteToFileAsync(string _path)
        {
            WriteResult wr = new WriteResult(this.m_fTotalWrite, out m_pWriteUpdate);
            Thread = new Thread(new ThreadStart(() =>
            {
                this.WriteToFile(_path);
                Thread.Abort();
            }));
            Thread.Start();
            return wr;
        }


        /// <summary>
        /// 同步加载Lua的String成C#结构加强版，速度要快些但有限制
        /// local table = {
        ///     ...
        /// }
        /// </summary>
        /// <param name="_path">写入路径</param>
        /// <param name="_tabName">Lua的table名称</param>
        public void WriteToFileEx(string _path, string _tabName = "UnknowName")
        {
            _BeginWriteFile(_path);
            m_pWriter.Write(string.Format("{0} = {{\n", _tabName));
            _WriteLuaTable(m_pWriter, this, 1, "    ");
            m_pWriter.Write("}\n");
            _EndWriteFile();
        }


        /// <summary>
        /// 异步加载Lua的String成C#结构加强版，速度要快些但有限制
        /// local table = {
        ///     ...
        /// }
        /// </summary>
        /// <param name="_lstr">存放路径/param>
        /// <param name="_tabName">Lua的table名称</param>
        /// <returns>返回解析结果</returns>
        public WriteResult WriteToFileExAsync(string _path, string _tabName = "UnknowName")
        {
            WriteResult wr = new WriteResult(this.m_fTotalWrite, out m_pWriteUpdate);
            Thread = new Thread(new ThreadStart(() =>
            {
                this.WriteToFileEx(_path, _tabName);
                Thread.Abort();
            }));
            Thread.Start();
            return wr;
        }


        public CSharpLuaTable()
        {
            _Init();
        }


        private CSharpLuaTable(string _lstr, Action _parser = null)
        {
            _Init();
            // lstr = Regex.Replace(lstr, @"[\n\r\t\s]", "");
            m_fTotalParse = _lstr.Length;
            ParseLuaTable(_lstr, _parser);
        }

        private CSharpLuaTable(float _fTotal, UpdateResult _ur)
        {
            _Init();
            m_pParseUpdate = _ur;
            m_fTotalParse = _fTotal;
        }

        private void _Init()
        {
            m_dictValue = new Dictionary<string, string>();
            m_dictTable = new Dictionary<string, CSharpLuaTable>();
            m_fCurrentPares = 0.0f;
            m_fCurrentWrite = 0.0f;
            m_fTotalParse = 0.0f;
            m_fTotalWrite = 0.0f;
        }


        #region Lua Table Parse
        private static string _Strcpy(string _src, int idx, int len)
        {
            char[] cstr = new char[len];
            Buffer.BlockCopy(_src.ToCharArray(), idx * sizeof(char), cstr, 0, len * sizeof(char));
            //Array.Copy(_src.ToCharArray(), idx, cstr, 0, len);
            return new string(cstr);
        }


        private bool _CheckValid(string _lstr, int _preIdx, int _nowIdx)
        {
            int count = 0;
            for (int i = _preIdx; i < _nowIdx; i++)
            {
                if ('\'' == _lstr[i] || '\"' == _lstr[i])
                {
                    count++;
                }
            }
            return (0 == (count & 0x01));
        }


        private int _MatchLuaTable(string _lstr, char _s, char _e, int _idx, ref string _strValue, bool _contain = true)
        {
            int count = 0;
            int endIdx = _idx;
            for (int i = _idx; i < _lstr.Length; i++)
            {
                endIdx = i;
                if (_s == _lstr[i])
                {
                    count += 1;
                }
                else if (_e == _lstr[i])
                {
                    count -= 1;
                    if (0 == count)
                        break;
                }
            }

            if (_contain)
                _strValue = _Strcpy(_lstr, _idx, endIdx - _idx + 1);
            else
                _strValue = _Strcpy(_lstr, _idx + 1, endIdx - _idx - 1);
            return endIdx;
        }

        private bool _MatchLuaValue(string _lstr, int _preIdx, int _nowIdx, ref string _strValue)
        {
            int sIdx = _preIdx;
            for (int i = _preIdx; i < _nowIdx; i++)
            {
                if ('\'' == _lstr[i] || '\"' == _lstr[i])
                {
                    sIdx = i + 1;
                    break;
                }
            }

            int eIdx = _nowIdx - 1;
            for (int i = _nowIdx - 1; i >= _preIdx; i--)
            {
                if ('\'' == _lstr[i] || '\"' == _lstr[i])
                {
                    eIdx = i - 1;
                    break;
                }
            }

            if (!_CheckValid(_lstr, _preIdx, _nowIdx))
                return false;

            _strValue = _Strcpy(_lstr, sIdx, eIdx - sIdx + 1);
            return true;
        }

        private bool _MatchLuaKey(string _lstr, int _preIdx, int _nowIdx, ref string _strKey)
        {
            int sIdx = _preIdx;
            for (int i = _preIdx; i < _nowIdx; i++)
            {
                if ('[' == _lstr[i] || '\"' == _lstr[i])
                    sIdx = i + 1;
                else
                    break;
            }

            int eIdx = _nowIdx - 1;
            for (int i = _nowIdx - 1; i >= _preIdx; i--)
            {
                if (']' == _lstr[i] || '\"' == _lstr[i])
                    eIdx = i - 1;
                else
                    break;
            }

            //if (!_CheckValid(_lstr, _preIdx, _nowIdx))
                //return false;

            _strKey = _Strcpy(_lstr, sIdx, eIdx - sIdx + 1);
            return true;
        }

        public void ParseLuaTable(string _lstr, Action _parser = null)
        {
            // bool bKey = true;
            string strKey = string.Empty;
            string strValue = string.Empty;
            int preIdx = 0;
            int key = 0;

            for (int i = 0; i < _lstr.Length; i++)
            {
                _UpdateCurrentPause(_parser);

                if (',' == _lstr[i])                    // 捕获Value
                {
                    if (!_MatchLuaValue(_lstr, preIdx, i, ref strValue))
                    {
                        strValue = string.Empty;
                        continue;
                    }

                    if (!m_dictValue.ContainsKey(strKey))
                    {
                        m_dictValue.Add(strKey, strValue);
                        m_fTotalWrite += 1;
                    }

                    strKey = string.Empty;
                    strValue = string.Empty;
                    preIdx = i + 1;
                }
                else if ('=' == _lstr[i])                // 捕获Key
                {
                    if(!_MatchLuaKey(_lstr, preIdx, i, ref strKey))
                    {
                        strKey = string.Empty;
                        continue;
                    }
                    // strKey = _Strcpy(_lstr, preIdx, i - preIdx);
                    preIdx = i + 1;
                }
                else if ('{' == _lstr[i])               // 捕获Table
                {
                    i = _MatchLuaTable(_lstr, '{', '}', i, ref strValue, false);
                    CSharpLuaTable t = ParseFromString(strValue, () => { _UpdateCurrentPause(_parser); });

                    if (string.Empty == strKey)
                        strKey = Convert.ToString(++key);

                    if (!m_dictTable.ContainsKey(strKey))
                    {
                        m_dictTable.Add(strKey, t);
                        _UpdateCurrentPause(_parser);
                        m_fTotalWrite += t.TotalWrite;

                        if (i + 1 < _lstr.Length && ',' == _lstr[i + 1])
                        {
                            i += 1;
                            _UpdateCurrentPause(_parser);
                        }
                    }

                    strKey = string.Empty;
                    strValue = string.Empty;
                    preIdx = i + 1;
                }  
            }
        }


        private void _UpdateCurrentPause(Action _parser = null)
        {
            m_fCurrentPares += 1;
            if (null != _parser)
                _parser();
            if (null != m_pParseUpdate && m_pParseUpdate(m_fCurrentPares))
                m_pParseUpdate = null;
        }


        private static string _Preproccess(string _lstr)
        {
            int sIdx = 0;
            int eIdx = _lstr.Length;

            for (int i = sIdx; i < eIdx; i++)
            {
                if ('{' == _lstr[i])
                {
                    sIdx = i + 1;
                    break;
                }
            }

            for (int i = eIdx - 1; i >= 0; i--)
            {
                if ('}' == _lstr[i])
                {
                    eIdx = i - 1;
                    break;
                }
            }
            return _Strcpy(_lstr, sIdx, eIdx - sIdx + 1);
        }


        private static CSharpLuaTable _ParseLuaTableEx(string _lstr)
        {
            _lstr = _Preproccess(_lstr);
            return ParseFromString(_lstr);
        }
        #endregion


        #region write to file

        private void _BeginWriteFile(string _path)
        {
            try 
            {
                m_pFile = new FileStream(@_path, FileMode.OpenOrCreate, FileAccess.ReadWrite);
                m_pFile.SetLength(0);
                m_pFile.Seek(0, SeekOrigin.Begin);
                m_pWriter = new StreamWriter(m_pFile, Encoding.UTF8);
            }
            catch
            {
                if (m_pThread.IsAlive)
                    m_pThread.Abort();
            }
        }

        private void _EndWriteFile()
        {
            try 
            {
                m_pWriter.Flush();
                m_pWriter.Dispose();
                m_pWriter = null;
                m_pFile.Dispose();
                m_pFile = null;
            }
            catch
            {
                if (m_pThread.IsAlive)
                    m_pThread.Abort();
            }
        }

        private static void _WriteTable(StreamWriter _writer, CSharpLuaTable _table, int deep = 0, string indent = "", Action _parser = null)
        {
            if (0 == _table.Table.Count)
                return;

            foreach (KeyValuePair<string, CSharpLuaTable> kv in _table.Table)
            {
                long lk;
                bool bNum = Int64.TryParse(kv.Key, out lk);
                string pWriteStrStart;
                string pWriteStrEnd;

                if (0 == deep)
                {
                    if (bNum)
                        pWriteStrStart = string.Format("{0}{1} = {{\n", indent, "Unknow_" + lk);
                    else
                        pWriteStrStart = string.Format("{0}{1} = {{\n", indent, kv.Key);

                    pWriteStrEnd = string.Format("{0}}}\n", indent);
                }
                else
                {
                    if (bNum)
                        pWriteStrStart = string.Format("{0}[{1}] = {{\n", indent, lk);
                    else
                        pWriteStrStart = string.Format("{0}[\"{1}\"] = {{\n", indent, kv.Key);

                    pWriteStrEnd = string.Format("{0}}},\n", indent);
                }

                _writer.Write(pWriteStrStart);

                _WriteLuaTable(_writer, kv.Value, deep + 1, indent + "    ", () => 
                {
                    _table._UpdateCurrentWrite(_parser); 
                });

                _writer.Write(pWriteStrEnd);
            }
        }

        private static void _WriteValue(StreamWriter _writer, CSharpLuaTable _table, int deep = 0, string indent = "", Action _parser = null)
        {
            if (0 == _table.Value.Count)
                return;

            foreach (KeyValuePair<string, string> kv in _table.Value)
            {
                long lk, lv;
                bool bv;
                bool bKeyNum = Int64.TryParse(kv.Key, out lk);
                bool bValueNum = Int64.TryParse(kv.Value, out lv);
                bool bValueBool = Boolean.TryParse(kv.Value, out bv);
                string pWriteStr;
                if (bKeyNum)
                {
                    if (bValueNum)
                        pWriteStr = string.Format("{0}[{1}] = {2},\n", indent, lk, lv);
                    else if (bValueBool)
                        pWriteStr = string.Format("{0}[{1}] = {2},\n", indent, lk, kv.Value);
                    else
                        pWriteStr = string.Format("{0}[{1}] = \"{2}\",\n", indent, lk, kv.Value);
                }
                else
                {
                    if (bValueNum)
                        pWriteStr = string.Format("{0}[\"{1}\"] = {2},\n", indent, kv.Key, lv);
                    else if (bValueBool)
                        pWriteStr = string.Format("{0}[\"{1}\"] = {2},\n", indent, kv.Key, kv.Value);
                    else
                        pWriteStr = string.Format("{0}[\"{1}\"] = \"{2}\",\n", indent, kv.Key, kv.Value);
                }
                _writer.Write(pWriteStr);
                _table._UpdateCurrentWrite(_parser);
            }
        }


        private static void _WriteLuaTable(StreamWriter _writer, CSharpLuaTable _table, int deep = 0, string indent = "", Action _parser = null)
        {
            _WriteValue(_writer, _table, deep, indent, _parser);
            _WriteTable(_writer, _table, deep, indent, _parser);
        }


        private void _UpdateCurrentWrite(Action _parser = null)
        {
            m_fCurrentWrite += 1;
            if (null != _parser)
                _parser();
            if (null != m_pWriteUpdate && m_pWriteUpdate(m_fCurrentWrite))
                m_pWriteUpdate = null;
        }

        #endregion


        #region Property Fields

        public float TotalParse
        {
            get
            {
                return m_fTotalParse;
            }

            private set
            {
                m_fTotalParse = value;
            }
        }

        public float CurrentPares
        {
            get
            {
                return m_fCurrentPares;
            }

            private set
            {
                m_fCurrentPares = value;
            }
        }

        public float TotalWrite
        {
            get
            {
                return m_fTotalWrite;
            }

            private set
            {
                m_fTotalWrite = value;
            }
        }

        public float CurrentWrite
        {
            get
            {
                return m_fCurrentWrite;
            }

            private set
            {
                m_fCurrentWrite = value;
            }
        }

        public Dictionary<string, CSharpLuaTable> Table
        {
            get
            {
                return m_dictTable;
            }

            private set
            {
                m_dictTable = value;
            }
        }

        public Dictionary<string, string> Value
        {
            get
            {
                return m_dictValue;
            }

            private set
            {
                m_dictValue = value;
            }
        }


        public Thread Thread
        {
            get
            {
                return m_pThread;
            }

            set
            {
                m_pThread = value;
            }
        }


        public WriteCountUpdater Updater
        {
            get
            {
                return m_updater;
            }

            set
            {
                m_updater = value;
            }
        }


        public CSharpLuaTable this[string index]
        {
            get
            {
                return m_dictTable[index];
            }
            set
            {
                m_dictTable[index] = value;
                value.Updater = UpdateWriteCount;
            }
        }


        public void UpdateWriteCount(int _offset)
        {
            m_fTotalWrite += _offset;
            if (null != Updater)
            {
                Updater(_offset);
            }
        }


        public string GetFields(string index)
        {
            return m_dictValue[index];
        }


        public bool SetFields(string index, string value)
        {
            if (m_dictValue.ContainsKey(index))
            {
                if (null == value)
                {
                    m_dictValue.Remove(index);
                    UpdateWriteCount(-1);
                }
                else
                {
                    m_dictValue[index] = value;
                }
            }
            else
            {
                m_dictValue[index] = value;
                UpdateWriteCount(1);
            }
            return true;
        }

        #endregion
    }
}
